﻿using System;
using System.Configuration;
using System.Configuration.Provider;
using System.IdentityModel.Claims;
using System.Web.Configuration;
using Altairis.IdentityToolkit.Configuration;

namespace Altairis.IdentityToolkit {

    /// <summary>
    /// Static class for exposing <see cref="IdentityLinkProvider"/> functionality to ASP.NET applications.
    /// </summary>
    public class IdentityLinkManager {

        static IdentityLinkManager() {
            // Get configuration info
            var config = ConfigurationManager.GetSection("altairis.identityToolkit/identityLink") as IdentityLinkConfiguration;
            if (config == null || config.Providers == null || config.Providers.Count == 0) throw new ProviderException("No identity link providers specified");

            // Instantiate the providers
            Providers = new IdentityLinkProviderCollection();
            ProvidersHelper.InstantiateProviders(config.Providers, Providers, typeof(IdentityLinkProvider));
            Providers.SetReadOnly();
        }

        /// <summary>
        /// Gets collection of identity link providers.
        /// </summary>
        /// <value>Collection of identity link providers.</value>
        public static IdentityLinkProviderCollection Providers { get; private set; }

        /// <summary>
        /// Resolves the link between given claim set and user name.
        /// </summary>
        /// <param name="claimSet">The claim set.</param>
        /// <returns>membership user name linked to given claim set. Returns <c>null</c> when no link was found.</returns>
        public static string ResolveLink(ClaimSet claimSet) {
            var p = Providers.Find(claimSet);
            if (p == null) throw new UnsupportedClaimSetException();
            return p.ResolveLink(claimSet);
        }

        /// <summary>
        /// Creates the link between given claim set and user name.
        /// </summary>
        /// <param name="claimSet">The claim set.</param>
        /// <param name="userName">Membership user name.</param>
        public static void CreateLink(ClaimSet claimSet, string userName) {
            var p = Providers.Find(claimSet);
            if (p == null) throw new UnsupportedClaimSetException();
            p.CreateLink(claimSet, userName);
        }

    }

    /// <summary>
    /// Class representing collection of identity link providers.
    /// </summary>
    public class IdentityLinkProviderCollection : ProviderCollection {
        
        /// <summary>
        /// Finds provider supporting the specified claim set.
        /// </summary>
        /// <param name="claimSet">The claim set.</param>
        /// <returns>Instance of <see cref="IdentityLinkProvider"/> supporting given <paramref name="claimSet"/>.</returns>
        public IdentityLinkProvider Find(ClaimSet claimSet) {
            foreach (IdentityLinkProvider p in this) {
                if (p.Supports(claimSet)) return p;
            }
            return null;
        }

        /// <summary>
        /// Adds a provider to the collection.
        /// </summary>
        /// <param name="provider">The provider to be added.</param>
        /// <exception cref="T:System.NotSupportedException">The collection is read-only.</exception>
        /// <exception cref="T:System.ArgumentNullException">
        /// 	<paramref name="provider"/> is null.</exception>
        /// <exception cref="T:System.ArgumentException">The <see cref="P:System.Configuration.Provider.ProviderBase.Name"/> of <paramref name="provider"/> is null.- or -The length of the <see cref="P:System.Configuration.Provider.ProviderBase.Name"/> of <paramref name="provider"/> is less than 1.</exception>
        /// <PermissionSet>
        /// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
        /// </PermissionSet>
        public override void Add(ProviderBase provider) {
            if (provider == null) throw new ArgumentNullException("The provider parameter cannot be null.");
            if (!(provider is IdentityLinkProvider)) throw new ArgumentException("The provider parameter must be of type IdentityLinkProvider.");
            base.Add(provider);
        }

        /// <summary>
        /// Gets the <see cref="IdentityLinkProvider"/> with the specified name.
        /// </summary>
        /// <value></value>
        new public IdentityLinkProvider this[string name] {
            get { return (IdentityLinkProvider)base[name]; }
        }

    }

}